﻿<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <title>TimetableCSP</title>
	<script src="codebase/dhtmlxscheduler.js" type="text/javascript" charset="utf-8"></script>
	<script src="codebase/dhtmlxcommon.js" type="text/javascript" charset="utf-8"></script>
	<script src="codebase/dhtmlxmenu.js" type="text/javascript" charset="utf-8"></script>
	<script src='codebase/ext/dhtmlxmenu_ext.js' type="text/javascript" charset="utf-8"></script>
	<script src='codebase/ext/dhtmlxscheduler_tooltip.js' type="text/javascript" charset="utf-8"></script>
	<script src="Scripts/jquery-1.4.1.min.js" type="text/javascript"></script>
	<script src="shadowbox/shadowbox.js" type="text/javascript"></script>
	<link href="shadowbox/shadowbox.css" rel="stylesheet" type="text/css" />
	<link href="codebase/skins/dhtmlxmenu_dhx_skyblue.css" rel="stylesheet" type="text/css" />
	<link rel="stylesheet" href="codebase/dhtmlxscheduler.css" type="text/css" media="screen" title="no title" charset="utf-8" />
	<link rel="stylesheet" href="codebase/ext/dhtmlxscheduler_ext.css" type="text/css" media="screen" title="no title" charset="utf-8" />
	<style type="text/css" media="screen">
		html, body{
			margin:0px;
			padding:0px;
			height:100%;
			overflow:hidden;
			font-family: tahoma, verdana;
			font-size: 10pt;
		}	
		
		#generateNewButton
		{
			width: 100%;
			text-align: center;
			height: 30px;
			margin-bottom: 12px;
		}
		#controlPanel
		{
			padding:4px;
			background-image: url('Images/ControlPanelBG.png');
			color: white;
			padding-left: 8px;
			padding-right: 8px;
		}
		#informationContainer
		{
			margin-top: 12px;
		}
		#informationLabel
		{
			font-weight: bold;
		}
		TD
		{
			vertical-align: top;
			font: tahoma, verdana;
		}
		.dhx_cal_event.pinned_event div
		{
			background-color: #3F6397; 
			color:white;
		}
	</style>
	<script type="text/javascript" charset="utf-8">

	var timeslots = null;
	var menu = null;
	var lastClickedEventId = null;

	function init()
	{
		//
		// Initialize the scheduler (calendar) component
		//
		scheduler.config.multi_day = true;
		scheduler.config.first_hour = 8;
		scheduler.config.last_hour = 22;
		scheduler.config.drag_move = true;
		scheduler.config.drag_resize = false;
		scheduler.config.drag_create = false;
		scheduler.config.dblclick_create = false;
		scheduler.config.edit_on_create = false;
		scheduler.config.details_on_create = false;
		scheduler.config.details_on_dblclick = false;
		scheduler.xy.nav_height = 0;
		scheduler.config.xml_date = "%Y-%m-%d %H:%i";
		//
		// Attach event handlers that prevent the default editing capabilities 
		//
		scheduler.attachEvent("onClick", function (event_id, native_event_object)
		{
			return false;
		});
		scheduler.attachEvent("onDblClick", function (event_id, native_event_object)
		{
			return false;
		});
		//
		// Attach an event handler for when event (classes) are dragged
		//
		scheduler.attachEvent("onEventChanged", function (event_id, event_object)
		{
			//
			// Snap the event/class to the appropriate timeslot
			//
			var slotIndex = FindClosestTimeslot(event_object.start_date);
			MoveToSlot(slotIndex, event_object);
			//
			// Pin the event/class and refresh the contents of the scheduler
			//
			var newTimeslot = (event_object.start_date.getDay() - 1) * timeslots.length + slotIndex;
			PinAndRefresh(event_id, newTimeslot);
		});
		//
		// Attach the context-menu event handler
		//
		scheduler.attachEvent("onContextMenu", function (event_id, native_event_object)
		{
			//
			// If event_id is null then the user clicked something other than an event/class
			//
			if (event_id)
			{
				//
				// If the class is already pinned, show the unpin command. Otherwise show the pin command.
				//
				lastClickedEventId = event_id;
				if (parseInt(scheduler.getEvent(event_id).pinned) > 0)
				{
					menu.showItem("unpin");
					menu.hideItem("pin");
				}
				else
				{
					menu.showItem("pin");
					menu.hideItem("unpin");
				}
				//
				// Show the context-menu
				//
				menu.showContextMenu(native_event_object.x, native_event_object.y);
			}
		});
		//
		// Add a template for tooltip text
		//
		scheduler.templates.tooltip_text = function (start, end, event)
		{
			return event.details;
		}
		//
		// Add a template that colors pinned events/classes differently
		//
		scheduler.templates.event_class = function (start, end, event)
		{
			if (parseInt(event.pinned))
			{
				return "pinned_event";
			}
		}
		//
		// By default the scheduler also displays the date on the week scale. Override that behavior to only show day names.
		//
		dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
		scheduler.templates.week_scale_date = function (date)
		{
			return dayNames[date.getDay()];
		}
		//
		// OK, we're now ready to display the scheduler
		//
		scheduler.init('scheduler_here', null, "week");
		//
		// Fetch the list of timeslots from the server
		//
		LoadTimeslots();
		//
		// Initialize the context-menu
		//
		InitMenu();
		//
		// Show a welcome message to the user
		//
		Shadowbox.init({
			skipSetup: true,
			displayNav: false,
			modal: true
		});
		var startOptions = {
			modal: false,
			displayNav: true
		};
		Shadowbox.open({ content: '<div id="welcome-msg" style="color: white; padding:10px; vertical-align: center">To get started click the "Generate new timetable" button.</div>',
			player: "html",
			title: "Welcome",
			height: 75,
			width: 200,
			options: startOptions
		});
	}

	function InitMenu()
	{
		//
		// Create and initialize the context menu control
		//
		menu = new dhtmlXMenuObject();
		menu.renderAsContextMenu();
		menu.addContextZone("scheduler_here");
		menu.setIconsPath("codebase/imgs/");
		menu.addNewChild(menu.topId, 0, "setClassroom", "Set classroom", false);
		menu.addNewChild(menu.topId, 1, "pin", "Pin", false, "push_pin.png");
		menu.addNewChild(menu.topId, 2, "unpin", "Unpin", false, "push_pin.png");
		menu.setItemDisabled("setClassroom"); // TODO: Enable when implemented
		menu.setAutoShowMode(false);
		menu.setAutoHideMode(false);
		menu.attachEvent("onClick", function (id)
		{
			switch (id)
			{
				case "pin":
					PinAndRefresh(lastClickedEventId, -1);
					break;
				case "unpin":
					UnpinAndRefresh(lastClickedEventId);
					break;
			}
		});
	}

	function PinAndRefresh(eventID, newTimeslot)
	{
		//
		// Send an AJAX request to move particular event/class to a new timeslot and pin it.
		//
		jQuery.getJSON("TimetableGenerator.aspx?Data=Pin&EventId=" + eventID + "&Timeslot=" + newTimeslot, null, function (data)
		{
			//
			// Did the pin operation fail? If so, display the error message.
			//
			if (!data.Success)
			{
				alert(data.Message);
			}
			//
			// Refresh the timetable (the contents of the scheduler) 
			//
			ShowTimetable();
			//
			// The server will include information about side-effects caused by the move (if any). 
			// Display that message if applicable.
			//
			if (data.Success && data.Message)
			{
				ShowInformation(data.Message);
			}
			else
			{
				HideInformation();
			}
		});
	}

	function UnpinAndRefresh(eventID)
	{
		//
		// Send an AJAX request to the server to unpin the specified event/class
		//
		jQuery.getJSON("TimetableGenerator.aspx?Data=Unpin&EventId=" + eventID, null, function (data)
		{
			//
			// Did the unpin operation fail? If so, display the error message.
			//
			if (!data.Success)
			{
				alert(data.Message);
				return;
			}
			//
			// Refresh the timetable (the contents of the scheduler) 
			//
			ShowTimetable();
		});
	}

	function MoveToSlot(slotIndex, eventObject)
	{
		//
		// The information about when/where timeslots begin and end is in minutes. We'll use the AdjustTime function to modify the
		// the time component of the start and end dates to coincide with the new timeslot.
		//
		var newStartDate = AdjustTime(eventObject.start_date, timeslots[slotIndex].Start);
		var newEndDate = AdjustTime(eventObject.end_date, timeslots[slotIndex].Stop);
		scheduler.setEventStartDate(eventObject.id, newStartDate);
		scheduler.setEventEndDate(eventObject.id, newEndDate);
	}

	function AdjustTime(date, minutes)
	{
		date.setHours(minutes / 60);
		date.setMinutes(minutes % 60);
		return date;
	}

	function FindClosestTimeslot(startDate)
	{
		//
		// Convert the time component of the date into minutes
		//
		var minutes = startDate.getMinutes() + startDate.getHours() * 60;
		//
		// Iterate through the timeslots and find the one that's closest to the specified start date
		//
		var closestTimeslot = -1;
		var leastDifference = 24 * 60;
		for (var index = 0; index < timeslots.length; index++)
		{
			var difference = Math.abs(timeslots[index].Start - minutes);
			if (difference < leastDifference)
			{
				closestTimeslot = index;
			}
			leastDifference = difference;
		}
		return closestTimeslot;
	}

	function ShowTimetable()
	{
		//
		// Remove all events from the scheduler component
		//
		scheduler.clearAll();
		//
		// Get the current view filter, if any (filters can be used to view a timetable for a certain teacher, course, classroom etc.)
		//
		var filter = GetViewFilter();
		//
		// Construct the URL to the page that will return the events (as xml)
		//
		var url = "TimetableGenerator.aspx?Data=Timetable&Filter=" + encodeURIComponent(filter);
		//
		// Set the scheduler to read-only mode if there's no filter. 
		// This is to prevent the user from dragging events when looking at the overview/summary.
		//
		scheduler.config.readonly = (filter.length == 0);
		//
		// Populate the scheduler with data.
		//
		scheduler.load(url);
	}

	function GetViewFilter()
	{
		var selectionList = document.getElementById("viewSelect");
		if (selectionList.selectedIndex < 1)
		{
			return "";
		}
		return selectionList.options[selectionList.selectedIndex].value;
	}

	function LoadTimeslots()
	{
		//
		// Load the list of timeslots from the server using AJAX
		//
		jQuery.getJSON("TimetableGenerator.aspx?Data=Timeslots", null, function (data)
		{
			for (var index = 0; index < data.length; index++)
			{
				//
				// The .NET TimeSpan objects are serialized into strings that don't lend themselves well to date/time calculations.
				// We'll convert them into a number of minutes to solve this.
				//
				data[index].Start = TimespanToMinutes(data[index].Start);
				data[index].Stop = TimespanToMinutes(data[index].Stop);
			}
			timeslots = data;
		});
	}

	function TimespanToMinutes(timespan)
	{
		var hPosition = timespan.indexOf("H");
		var mPosition = timespan.indexOf("M");
		var minutes = 0;
		if (mPosition != -1)
		{
			minutes = parseInt(timespan.substring(hPosition + 1, mPosition));
		}
		else
		{
			mPosition = timespan.length;
		}
		var hours = parseInt(timespan.substring(2, hPosition));
		return minutes + hours * 60;
	}

	function LoadViewOptions(callbackFunction)
	{
		//
		// Get the list of courses, classrooms and teachers from the server using AJAX
		//
		jQuery.getJSON("TimetableGenerator.aspx?Data=ViewOptions", null, function (data) { LoadViewOptionsCallback(data, callbackFunction); });
	}

	function LoadViewOptionsCallback(data, callbackFunction)
	{
		//
		// Disable the 'onchange' event handler of the selection list while we populate it.
		//
		var selectionList = document.getElementById("viewSelect");
		selectionList.onchange = null;
		//
		// Remove all existing options from the selection list
		//
		selectionList.innerHTML = "";
		//
		// Add the 'Overview' option
		//
		selectionList.appendChild(new Option("Overview", "Overview"));
		//
		// Add the 'By classroom' group and populate it
		//
		AddSelectionGroup(selectionList, "By classroom", data.Classrooms, "classroom:");
		//
		// Add the 'By course' group and populate it
		//
		AddSelectionGroup(selectionList, "By course", data.Courses, "course:");
		//
		// Add the 'By teacher' group and populate it
		//
		AddSelectionGroup(selectionList, "By teacher", data.Teachers, "teacher:");
		//
		// Select the first option (Overview)
		//
		selectionList.selectedIndex = 0;
		//
		// Enable the onchange event again
		//
		selectionList.onchange = ViewChanged;
		//
		// Call a callback function if on has been supplied
		//
		if (callbackFunction)
		{
			callbackFunction();
		}
	}

	function AddSelectionGroup(selectionList, label, values, prefix)
	{
		var optionGroup = document.createElement("optgroup");
		optionGroup.label = label;
		jQuery.each(values, function (index, value)
		{
			optionGroup.appendChild(new Option(value, prefix + value));
		});
		selectionList.appendChild(optionGroup);
	}

	function ViewChanged()
	{
		//
		// The user has selected a different view (Overview, By course, By classroom etc).
		// Update the timetable accordingly
		//
		ShowTimetable(false);
	}

	function GenerateButtonClicked()
	{
		//
		// Hide the info-panel
		//
		HideInformation();
		//
		// Show a progress indicator
		//
		Shadowbox.open({
			content: "Images/lolsma.gif",
			player: "img",
			title: "CAN HAS TIMETABLE?"
		});
		//
		// Ask the server to generate a new timetable
		//
		jQuery.getJSON("TimetableGenerator.aspx?Data=GenerateNew", null, function (data)
		{
			//
			// Hide the progress indicator
			//
			Shadowbox.close();
			//
			// Did the operation fail? If so, display the error message.
			//
			if (!data.Success)
			{
				alert(data.Message);
				return;
			}
			//
			// Populate the view filter selection list
			//
			LoadViewOptions(ShowTimetable);
		});
	}

	function ShowInformation(text)
	{
		$("#informationContents").html(text);
		$("#viewSelect").height(550 - $("#informationContainer").innerHeight());
		$("#informationContainer").fadeIn(500);
	}

	function HideInformation()
	{
		$("#viewSelect").height("550px");
		$("#informationContainer").fadeOut(500);
	}
</script>
</head>
<body onload="init();">
	<table border="0" cellpadding="0" cellspacing="0" style="height:100%; width:100%">
		<tr>
			<td id="scheduler_here" class="dhx_cal_container" style="width:80%;">
				<div class="dhx_cal_navline" style="display: none">
					<div class="dhx_cal_prev_button">&nbsp;</div>
					<div class="dhx_cal_next_button">&nbsp;</div>
					<div class="dhx_cal_today_button"></div>
					<div class="dhx_cal_date"></div>
					<div class="dhx_cal_tab" name="day_tab" style="right:204px;"></div>
					<div class="dhx_cal_tab" name="week_tab" style="right:140px;"></div>
					<div class="dhx_cal_tab" name="month_tab" style="right:76px;"></div>
				</div>
				<div class="dhx_cal_header">
				</div>
				<div class="dhx_cal_data">
				</div>
			</td>
			<td id="controlPanel">
				<input type="button" id="generateNewButton" value="Generate new timetable" onclick="GenerateButtonClicked();" /><br />
				<b>View:</b><br />
				<select id="viewSelect" size="38" style="width: 100%; height: 550px;">
				</select>
				<div id="informationContainer" style="display:none;">
					<div id="informationLabel">Information:</div>
					<div id="informationContents"></div>
				</div>
			</td>
		</tr>
	</table>
</body>
</html>
